﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using WiimoteLib;
using System.Runtime.InteropServices;


namespace Fitts__law_experiment_with_a_wiimote
{
    public partial class ExperimentForm : Form
    {
        [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
        public static extern void mouse_event(long dwFlags, long dx, long dy, long cButtons, long dwExtraInfo);

        protected bool dontExit = false;
        private const int MOUSEEVENTF_LEFTDOWN = 0x02;
        private const int MOUSEEVENTF_LEFTUP = 0x04;
        private const int MOUSEEVENTF_RIGHTDOWN = 0x08;
        private const int MOUSEEVENTF_RIGHTUP = 0x10;
		protected int desiredNumberOfClicks = 16;

        private int Led = 1;
        private int direction = 1;
        private bool AButtonDown = false;
        private Experiment currentExperiment;

        public Experiment experiment
        {
            get {
                if (currentExperiment == null)
                    currentExperiment = new Experiment("Null", PrimaryButton, SecondaryButton);
                return currentExperiment; 
            }
        }

        private Wiimote Mote
        {
            get { return Program.Wiimote; }
        }

        


        #region "form"
        public ExperimentForm()
        {
            InitializeComponent();
            Mote.SetLEDs(false, false, false, false);
            Mote.WiimoteChanged += UpdateState;
            SetExperiment(new Experiment("default", PrimaryButton, SecondaryButton));
        }

        private void ExperimentForm_Load(object sender, EventArgs e)
        {
				BatteryLevel.Value = (int) Math.Min(100, Mote.WiimoteState.Battery);
				ledTimer.Start();
				TRefreshIR.Start();
				this.WindowState = FormWindowState.Maximized;
				MExperiment.Text = this.Text;
        }

		private void ExperimentForm_FormClosing(object sender, FormClosingEventArgs e)
		{
			if (!dontExit)
			{
				if (experiment.IsExperimentRunning)
				{
					if (MessageBox.Show("An experiment is in progress. Are you sure you want to exit the application?", "Experiment", MessageBoxButtons.YesNoCancel) != DialogResult.Yes)
					{
						e.Cancel = true;
						return;
					}
				}
				experiment.End();
				Application.Exit();
			}
		}
        #endregion


        #region "wiimote"
        private void UpdateState(object sender, WiimoteChangedEventArgs args)
        {
            if (args.WiimoteState.ButtonState.A)
            {
                if (!AButtonDown)
                {
					AButtonDown = true;
                    mouse_event(MOUSEEVENTF_LEFTDOWN, Cursor.Position.X, Cursor.Position.Y, 0, 0);
                }
            }
            else if (AButtonDown)
            {
				AButtonDown = false;
                mouse_event(MOUSEEVENTF_LEFTUP, Cursor.Position.X, Cursor.Position.Y, 0, 0);
            }
        }

        private void timer1_Tick(object sender, EventArgs e)
        {
            Led += direction;
            if ((Led == 1) || (Led == 4))
                direction = -direction;
            switch (Led)
            {
                case 1:
                    Mote.SetLEDs(true, false, false, false);
                    break;
                case 2:
                    Mote.SetLEDs(false, true, false, false);
                    break;
                case 3:
                    Mote.SetLEDs(false, false, true, false);
                    break;
                case 4:
                    Mote.SetLEDs(false, false, false, true);
                    break;
            }
        }

        private void TRefreshIR_Tick(object sender, EventArgs e)
        {
            IRSensor IR1 = Mote.WiimoteState.IRState.IRSensors[0];
            IRSensor IR2 = Mote.WiimoteState.IRState.IRSensors[1];
            int safeMarginH = 300;
            int safeMarginV = 300;
            if (IR1.Found)
            {
                LIR1.Text = String.Format("IR1: ( {0:0.00} , {0:0.00} )", IR1.Position.X, IR1.Position.Y);
                Cursor.Position = new System.Drawing.Point(
                    (int)((Screen.PrimaryScreen.Bounds.Width + safeMarginH * 2) * (1 - IR1.Position.X) - safeMarginH),
                    (int)((Screen.PrimaryScreen.Bounds.Height + safeMarginV * 2) * (IR1.Position.Y) - safeMarginV)
                 );
            }
            else
                LIR1.Text = "IR1: ( ? , ? )";

            if (IR2.Found)
                LIR2.Text = String.Format("IR1: ( {0:0.00} , {0:0.00} )", IR2.Position.X, IR2.Position.Y);
            else
                LIR2.Text = "IR2: ( ? , ? )";

            if (IR1.Found)
            {
                AimLabel.Visible = false;
            }
            else
                AimLabel.Visible = true;

        }

        #endregion

        #region "experiment"
        protected void SetExperiment(Experiment experiment)
        {
            this.currentExperiment = experiment;
			this.Text = "Fitts' law - " + experiment.Name;
        }

        private void BeginExperiment()
        {
            if (currentExperiment == null)
                SetExperiment(new Experiment("default", PrimaryButton, SecondaryButton));
            experiment.Start();
            ExperimentTimer.Enabled = true;
            ExperimentTimer.Start();
			PrimaryButton.Text = "Click";
        }

        private void EndExperiment()
        {
            ExperimentTimer.Enabled = false;
            ExperimentTimer.Stop();
            experiment.End();
            String file = experiment.Name + " (" + DateTime.Now.ToShortDateString().Replace("/", "-") + " " + DateTime.Now.ToShortTimeString().Replace(":", ".") + ").exd";
            try
            {
                experiment.SaveToFile(file);
            }
            catch (Exception ex)
            { MessageBox.Show(ex.Message); }
        }

		private void FinishExperiment()
		{
			doneLabel.Visible = true;
			proceedButton.Visible = true;
			EndExperiment();
			PrimaryButton.Visible = false;
			SecondaryButton.Visible = false;
			OnExperimentFinished();
		}

		protected void OnExperimentFinished()
		{
			
		}

        private void ExperimentTimer_Tick(object sender, EventArgs e)
        {
            TimeLabel.Text = "Elapsed time: " + secondsToString((int)(experiment.getTimestamp() / 10000000));
        }

		private void proceedButton_Click(object sender, EventArgs e)
		{
			onProceedExperiment();
		}

		protected virtual void onProceedExperiment()
		{
			MessageBox.Show("Thank you for particpating in this experiment.", "Experiment", MessageBoxButtons.OK);
			Application.Exit();
		}

        #endregion

        #region "MOUSE STUFF"
        protected void onMouseButtonEnter()
        {
            // Mote.SetRumble(true);
        }
        protected void onMouseButtonLeave()
        {
            // Mote.SetRumble(false);
        }
		protected virtual void onMousePrimaryButtonClick()
		{
		}

		protected virtual void onMouseSecondaryButtonClick()
		{
		}

        #region "form"
        private void ExperimentForm_MouseDown(object sender, MouseEventArgs e)
        {
            if (experiment.IsExperimentRunning)
            {
				System.Drawing.Point pos = this.PointToScreen(e.Location);
                experiment.AddMouseDownEvent(pos.X, pos.Y);
				updateStatsLabel();
            }
        }

        private void ExperimentForm_MouseUp(object sender, MouseEventArgs e)
        {
            if (experiment.IsExperimentRunning)
            {
                System.Drawing.Point pos = this.PointToScreen(e.Location);
                experiment.AddMouseUpEvent(pos.X, pos.Y);
				updateStatsLabel();
            }
        }

        private void ExperimentForm_MouseMove(object sender, MouseEventArgs e)
        {
            if (experiment.IsExperimentRunning)
            {
                System.Drawing.Point pos = this.PointToScreen(e.Location);
                experiment.AddMouseMovedEvent(pos.X, pos.Y);
            }
        }
        #endregion
        #region "Primary"

        private void PrimaryButton_MouseDown(object sender, MouseEventArgs e)
        {
            if (experiment.IsExperimentRunning)
            {
				System.Drawing.Point pos = PrimaryButton.PointToScreen(e.Location);
                experiment.AddMouseDownEvent(pos.X, pos.Y);
				updateStatsLabel();
            }
        }

        private void PrimaryButton_MouseUp(object sender, MouseEventArgs e)
        {
			if (!experiment.IsExperimentRunning)
			{
				BeginExperiment();
			}
			System.Drawing.Point pos = PrimaryButton.PointToScreen(e.Location);
			experiment.AddMouseUpEvent(pos.X, pos.Y);
			updateStatsLabel();
			if (!experiment.MissedLastButton)
			{
				SecondaryButton.Enabled = true;
				PrimaryButton.Enabled = false;
				experiment.Target = 2;
				onMousePrimaryButtonClick();
			}

			if ((experiment.ClickCount - experiment.MissedClicksCount) >= desiredNumberOfClicks)
				FinishExperiment();
        }

        private void PrimaryButton_MouseEnter(object sender, EventArgs e)
        {
            onMouseButtonEnter();
        }

        private void PrimaryButton_MouseLeave(object sender, EventArgs e)
        {
            onMouseButtonLeave();
        }
        #endregion
        #region "Secondary"
        private void SecondaryButton_MouseDown(object sender, MouseEventArgs e)
        {
            if (experiment.IsExperimentRunning)
            {
                System.Drawing.Point pos = SecondaryButton.PointToScreen(e.Location);
                experiment.AddMouseDownEvent(pos.X, pos.Y);
				updateStatsLabel();
            }
        }

        private void SecondaryButton_MouseUp(object sender, MouseEventArgs e)
        {
            if (experiment.IsExperimentRunning)
            {
				System.Drawing.Point pos = SecondaryButton.PointToScreen(e.Location);
                experiment.AddMouseUpEvent(pos.X, pos.Y);
				updateStatsLabel();
				if (!experiment.MissedLastButton)
				{
					PrimaryButton.Enabled = true;
					SecondaryButton.Enabled = false;
					experiment.Target = 1;
					onMouseSecondaryButtonClick();
				}
				
				if ((experiment.ClickCount - experiment.MissedClicksCount) >= desiredNumberOfClicks)
					FinishExperiment();
            }
        }
        #endregion
        #endregion

		private void updateStatsLabel()
		{
			statsLabel.Text = "Clicks: " + experiment.ClickCount + ", missed clicks: " + experiment.MissedClicksCount + ", Target: button " + experiment.Target ;
		}



        #region "Experiment menu"
        private void openExperimentViewerToolStripMenuItem_Click(object sender, EventArgs e)
        {
            ExperimentViewer form = new ExperimentViewer();
            form.ShowDialog();
        }

        private void endToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (experiment.IsExperimentRunning)
            {
                EndExperiment();
            }
        }
        #endregion


        #region "UTIL"
        protected static String secondsToString(int seconds)
        {
            TimeSpan ts = new TimeSpan(0, 0, 0, seconds, 0);
            int Hours = (int)ts.TotalHours;

            return string.Format("{0}:{1}:{2}", Hours, ts.Minutes, ts.Seconds);
        }
        #endregion
    }
}
